
#include "mdio.h"

#include "string.h"
#include "higmac.h"
#include "util.h"
#include "linux/kernel.h"
#include "gmac.h"
#include "ctrl.h"
#include "eth_drv.h"
#include "eth_phy.h"
#include "eth_phy_dev.h"
#include "netdev.h"
#include "los_task.h"
#include "linux/interrupt.h"
#include "linux/timer.h"

#include "los_sys_pri.h"

#include "los_list.h"
#include "linux/module.h"

#define PHY_ADDR_SET     0x129B5B3
#define PHY_ADDR_NOT_SET 0x5a5aa5a5
#define INVALID_PHY_ADDR 0x12F358F
#define PHY_MODE_SET     0x12B63D0
#define PHY_MODE_NOT_SET 0x5a5aa5a5
#define INVALID_PHY_MODE 0x12F5D09

int g_user_set_phy_addr = PHY_ADDR_NOT_SET;
int g_phy_addr_val = INVALID_PHY_ADDR;
int g_user_set_phy_mode = PHY_MODE_NOT_SET;
int g_phy_mode_val = INVALID_PHY_MODE;

#define HIGMAC_DESC_SIZE (HIGMAC_HWQ_TX_BQ_DEPTH * sizeof(struct higmac_desc))

#define debug1_printf PRINTK

struct higmac_platform_data *g_stHigmacPlatformData = NULL;
void _eth_drv_init(struct los_eth_driver *sc, unsigned char *mac)
{
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)sc->driver_context;

   (drv_sc->funs->eth_drv->init)(sc, mac);
}

void _eth_drv_recv(struct los_eth_driver *sc, int total_len)
{
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)sc->driver_context;

   (drv_sc->funs->eth_drv->recv)(sc, total_len);
}

#define mdio_bus_0 "mdio0"
#define mdio_bus_1 "mdio1"

struct higmac_board_info higmac_board_info[MAX_GMAC_NUMS] = {
    {
        .gmac_name = "gmac0",
        .mii_name = mdio_bus_0,
        .phy_addr = 1,
        // .phy_intf    = CONFIG_HIGMAC_PHY0_INTERFACE_MODE,
    },
#if (CONFIG_GMAC_NUMS > 1)
    {
        .gmac_name = "gmac1",
        .mii_name = mdio_bus_0,
        .phy_addr = CONFIG_HIGMAC_PHY1_ADDR,
        .phy_intf = CONFIG_HIGMAC_PHY1_INTERFACE_MODE,
    }
#endif
};

const char *mii_to_str[] = {
    [0] = "mii",
    [1] = "rmii",
    [2] = "rgmii"
};
static char *mdio_bus[MAX_GMAC_NUMS] = {
    mdio_bus_0,
    mdio_bus_1
};

struct los_sk_buff *los_alloc_skb(unsigned int length)
{
    struct los_sk_buff *skb = NULL;
    unsigned int size = SKB_DATA_ALIGN(length + NET_SKB_PAD);

    skb = malloc(sizeof(struct los_sk_buff));
    if (!skb) {
        return NULL;
    }

    skb->data = memalign(CACHE_ALIGNED_SIZE, size);
    if (!skb->data) {
        return NULL;
    }

    skb->head = skb->data;
    skb->len = 0;
    skb->truesize = size;

    return skb;
}

struct los_sk_buff *los_alloc_skb_ip_align(unsigned int length)
{
    struct los_sk_buff *skb = los_alloc_skb(length);
    if (NET_IP_ALIGN && skb) {
        skb->data += NET_IP_ALIGN;
    }
    return skb;
}

void los_free_skb(struct los_sk_buff *skb)
{
    if (!skb) {
        return;
    }

    if (!skb->head) {
        return;
    }

    free(skb->head);
    free(skb);
}

void net_dma_cache_inv(void *addr, unsigned int size)
{
    AARCHPTR start = (AARCHPTR)addr & ~(CACHE_ALIGNED_SIZE - 1);
    AARCHPTR end = (AARCHPTR)addr + size;

    end = ALIGN(end, CACHE_ALIGNED_SIZE);

    dma_cache_inv(start, end);
}

void net_dma_cache_clean(void *addr, unsigned int size)
{
    AARCHPTR start = (AARCHPTR)addr & ~(CACHE_ALIGNED_SIZE - 1);
    AARCHPTR end = (AARCHPTR)addr + size;

    end = ALIGN(end, CACHE_ALIGNED_SIZE);

    dma_cache_clean(start, end);
}

/* default, disable autoeee func */
static bool enable_autoeee;

static void higmac_set_mac(higmac_priv_t *priv, UINT8 *enaddr)
{
    higmac_hw_set_mac_addr(&(g_stHigmacPlatformData[priv->index].stNetdevLocal), enaddr);
}

static void higmac_stop(struct los_eth_driver *sc)
{
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)sc->driver_context;
    higmac_priv_t *priv = (higmac_priv_t *)drv_sc->driver_private;

    higmac_port_disable(&(g_stHigmacPlatformData[priv->index].stNetdevLocal));
}

static void higmac_start(struct los_eth_driver *sc, unsigned char *enaddr, int flags)
{
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)sc->driver_context;
    higmac_priv_t *priv = (higmac_priv_t *)drv_sc->driver_private;

    // diag_printf("\nxhl -- higmac_start\n");
    higmac_port_enable(&(g_stHigmacPlatformData[priv->index].stNetdevLocal));
}

extern void hal_interrupt_mask(unsigned int vector);
extern int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
                       const char *name, void *dev);
extern void hal_interrupt_unmask(unsigned int vector);

static void higmac_deliver(struct los_eth_driver *sc);
void higmac_eth_isr(int irq, void *data)
{
    struct los_eth_driver *sc = (struct los_eth_driver *)data;
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)sc->driver_context;
    higmac_priv_t *priv = (higmac_priv_t *)drv_sc->driver_private;
    // struct higmac_netdev_local *ld = &netdev_local;

    hal_interrupt_mask(priv->intr_vector);
    // higmac_writel(ld, ~0, RAW_PMU_INT);
    LOS_EventWrite(&(g_stHigmacPlatformData[priv->index].stEvent), EVENT_NET_TX_RX);

    // hal_interrupt_unmask(NUM_HAL_INTERRUPT_GSF);
}

void higmac_eth_dsr(struct los_eth_driver *sc)
{
    UINT32 uwRet;
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)sc->driver_context;
    higmac_priv_t *priv = (higmac_priv_t *)drv_sc->driver_private;

    while (1) {
        uwRet = LOS_EventRead(&(g_stHigmacPlatformData[priv->index].stEvent), EVENT_NET_TX_RX,
                              LOS_WAITMODE_OR | LOS_WAITMODE_CLR, LOS_WAIT_FOREVER);
        if (uwRet & EVENT_NET_TX_RX) {
           (drv_sc->funs->deliver)(sc);
        }
    }
}

#define ETH_STACKSIZE 0x20000
int create_eth_irq_thread(struct los_eth_driver *sc)
{
    TSK_INIT_PARAM_S stSdTask;
    UINT32 uwRet;
    UINT32 uwEthTask;

    memset(&stSdTask, 0, sizeof(TSK_INIT_PARAM_S));
    stSdTask.pfnTaskEntry = (TSK_ENTRY_FUNC)higmac_eth_dsr;
    stSdTask.auwArgs[0] = (AARCHPTR)sc;
    stSdTask.uwStackSize = ETH_STACKSIZE;
    stSdTask.pcName = "eth_irq_Task";
    stSdTask.usTaskPrio = 3;
    stSdTask.uwResved = LOS_TASK_STATUS_DETACHED;
    uwRet = LOS_TaskCreate(&uwEthTask, &stSdTask);

    if (LOS_OK != uwRet) {
        return uwRet;
    }
    return 0;
}

#define PHY_ADDR 1

#define HGMCPKG_DEVS_ETH_ARM_HI3516A_MACADDR \
    {                                        \
        0x00, 0x19, 0xb1, 0x6c, 0xa1, 0x25   \
    }

HISI_NET_LOCK_T(eth_crg_lock);

#define PHY_ID_INVALID(id) (((id & 0x1fffffff) == 0x1fffffff) ||  \
                            ((id & 0xfffffff0) == 0xfffffff0) ||  \
                            (id == 0) ||  \
                            (id == 0xffff0000) ||  \
                            (id == 0x0000ffff))

static int scan_phy_id(struct higmac_netdev_local *ld, int addr)
{
    int phy_id;
    int val;

    val = higmac_mdio_read(ld, addr, PHY_ID1);
    phy_id = val << 16;
    val = higmac_mdio_read(ld, addr, PHY_ID2);
    phy_id |= val;

    return phy_id;
}

bool higmac_hw_init(struct platform_device *dev)
{
    struct los_eth_driver *p_stDriverData = (struct los_eth_driver *)dev_get_drvdata(&(dev->dev));
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)(p_stDriverData->driver_context);
    higmac_priv_t *priv = (higmac_priv_t *)drv_sc->driver_private;
    struct higmac_netdev_local *ld = &(g_stHigmacPlatformData[priv->index].stNetdevLocal);

    unsigned char enaddr[6] = HGMCPKG_DEVS_ETH_ARM_HI3516A_MACADDR;
    unsigned int phy_state = 0;
    unsigned int id;
    int ret;
    int addr;

    PRINTK("\nHisi_eth init begin.\n");

    HISI_NET_LOCK_INIT(&eth_crg_lock);
    HISI_NET_LOCK_INIT(&(ld->rx_lock));
    HISI_NET_LOCK_INIT(&(ld->tx_lock));
    ld->gmac_iobase = (char *)(long)priv->base;

    higmac_hw_mac_core_reset(ld);
    higmac_hw_mac_core_init(ld);

    if (g_user_set_phy_mode == PHY_MODE_SET) {
        priv->phy->phy_mode = g_phy_mode_val;
        PRINTK("hisi_eth: User set phy mode=%s\n",
               phy_modes(priv->phy->phy_mode));
    } else {
        priv->phy->phy_mode = ld->phy_mode;
        PRINTK("hisi_eth: User did not set phy mode, use default=%s\n",
               phy_modes(priv->phy->phy_mode));
    }

    if (!priv->phy->init_done) {
        higmac_hw_external_phy_reset();

        priv->phy->init_done = true;

        if (g_user_set_phy_addr == PHY_ADDR_SET) {
            priv->phy->phy_addr = g_phy_addr_val;
            PRINTK("hisi_eth: User set phy addr=%d\n", priv->phy->phy_addr);

            id = scan_phy_id(ld, priv->phy->phy_addr);
            if (PHY_ID_INVALID(id)) {
                PRINTK("Can't find PHY device - id: %x\n", id);
                priv->phy->init_done = false;
                goto err_out;
            }
        } else {
            PRINTK("hisi_eth: User did not set phy addr, auto scan...\n");

            for (addr = MAX_PHY_ADDR; addr >= 0; addr--) {
                id = scan_phy_id(ld, addr);
                if (PHY_ID_INVALID(id)) {
                    continue;
                }
                break;
            }

            if (addr < 0) {
                PRINTK("Can't find PHY device - id: %x\n", id);
                priv->phy->init_done = false;
                goto err_out;
            }

            priv->phy->phy_addr = addr;
        }
        ld->phy_id = id;
    }

    PRINTK("Detected phy addr %d, phyid: 0x%x\n", priv->phy->phy_addr, ld->phy_id);

    if (!priv->phy->init_done) {
        PRINTK("PHY state without init on PHY: %x\n", priv->phy);
        goto err_out;
    }

    higmac_get_phy_stat(ld, priv->phy, &phy_state);

    ret = higmac_init_hw_desc_queue(ld);
    if (ret) {
        PRINTK("%s: initial hw descriptor failed.\n", __func__);
        goto err_out;
    }

    higmac_feed_hw(ld, priv);

    create_eth_irq_thread(p_stDriverData);

    eth_random_addr(enaddr);
    higmac_hw_set_mac_addr(ld, enaddr);

    higmac_hw_desc_enable(ld);
    _eth_drv_init(p_stDriverData, enaddr);

    init_timer(&priv->phy_timer);
    priv->phy_timer.function = phy_state_machine;
    priv->phy_timer.data = (unsigned long)p_stDriverData;
    priv->phy_timer.expires = LOS_MS2Tick(PHY_STATE_TIME);

    add_timer(&priv->phy_timer);

    // clear all interrupts
    higmac_writel(ld, ~0, RAW_PMU_INT);

    // request_irq(NUM_HAL_INTERRUPT_SDIO, hisd_irq, 0, "MMC-SDIO", &host[0]);
    request_irq(priv->intr_vector, (irq_handler_t)higmac_eth_isr, 0, "ETH", p_stDriverData);
    hal_interrupt_unmask(priv->intr_vector);
    higmac_irq_enable(ld, RX_BQ_IN_INT | RX_BQ_IN_TIMEOUT_INT
                      | TX_RQ_IN_INT | TX_RQ_IN_TIMEOUT_INT);
    // debug1_printf("\n eth0, xhl, hello--------end\n");

    return true;
err_out:
    return false;
}

// ===========================================================================
// This function is called for low level "control" operations
// ===========================================================================
static int higmac_control(struct los_eth_driver *sc, unsigned long key,
                          void *data, int length)
{
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)sc->driver_context;
    higmac_priv_t *priv = (higmac_priv_t *)drv_sc->driver_private;
    switch (key) {
        case ETH_DRV_SET_MAC_ADDRESS: {
            if (length >= ETHER_ADDR_LEN) {
                higmac_stop(sc);

                UINT8 *enaddr = (UINT8 *)data;
                debug1_printf("HI3616A_ETH: %02x:%02x:%02x:%02x:%02x:%02x\n",
                              enaddr[0], enaddr[1], enaddr[2],
                              enaddr[3], enaddr[4], enaddr[5]);

                higmac_set_mac(priv, enaddr);
                higmac_start(sc, enaddr, 0);
                return 0;
            }
            return 1;
        }

        default:
        {
            diag_printf("%s.%d: key %lx\n", __FUNCTION__, __LINE__, key);
            return (1);
        }
    }  // switch (key)
}

extern int higmac_xmit_release_skb(struct higmac_netdev_local *ld,
                                   struct los_eth_driver *sc,
                                   struct higmac_priv_s *priv);

static int higmac_can_send(struct los_eth_driver *sc)
{
    int can_send;
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)sc->driver_context;
    higmac_priv_t *priv = (higmac_priv_t *)drv_sc->driver_private;
    struct higmac_netdev_local *ld = &(g_stHigmacPlatformData[priv->index].stNetdevLocal);
    unsigned int tx_bq_wr_offset;
    unsigned int tx_bq_wr_pos;
    unsigned int tx_bq_wr_next_pos;
    unsigned int tx_rq_rd_offset;
    unsigned int tx_rq_rd_pos;

    higmac_xmit_release_skb(ld, sc, priv);

    HISI_NET_LOCK_GET(&(ld->tx_lock));
    tx_bq_wr_offset = higmac_readl_bits(ld, TX_BQ_WR_ADDR,
                                        BITS_TX_BQ_WR_ADDR); /* software write pointer */
    tx_rq_rd_offset = higmac_readl_bits(ld, TX_RQ_RD_ADDR,
                                        BITS_TX_RQ_RD_ADDR); /* software write pointer */

    tx_bq_wr_pos = tx_bq_wr_offset >> DESC_BYTE_SHIFT;
    tx_bq_wr_next_pos = (tx_bq_wr_pos + 1) % HIGMAC_HWQ_TX_BQ_DEPTH;
    tx_rq_rd_pos = tx_rq_rd_offset >> DESC_BYTE_SHIFT;
    if (tx_bq_wr_next_pos == tx_rq_rd_pos) {
        UINT32 uwRet;
        can_send = 0;
        ld->tx_busy = 1;
        HISI_NET_LOCK_PUT(&(ld->tx_lock));
        hal_interrupt_unmask(priv->intr_vector);
        while (1) {
            uwRet = LOS_EventRead(&(g_stHigmacPlatformData[priv->index].stEvent)
                                  , EVENT_NET_CAN_SEND, LOS_WAITMODE_OR | LOS_WAITMODE_CLR, LOS_WAIT_FOREVER);
            if (uwRet & EVENT_NET_CAN_SEND) {
                can_send = 1;
                hal_interrupt_mask(priv->intr_vector);
                HISI_NET_LOCK_GET(&(ld->tx_lock));
                break;
            }
        }
    } else {
        can_send = 1;
    }
    HISI_NET_LOCK_PUT(&(ld->tx_lock));

    return (can_send);
}

static unsigned int tx_int_times;
extern int higmac_xmit_gso(struct higmac_netdev_local *ld,
                           unsigned int pos, struct pbuf *p,
                           struct higmac_tso_desc *tx_bq_desc);

static void higmac_send(struct los_eth_driver *sc, struct pbuf *p)
{
    int tx_bq_wr_offset, tx_bq_rd_offset, tmp, pos;
    struct higmac_desc *tx_bq_desc;
    // int i;
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)sc->driver_context;
    higmac_priv_t *priv = (higmac_priv_t *)drv_sc->driver_private;
    struct higmac_netdev_local *ld = &(g_stHigmacPlatformData[priv->index].stNetdevLocal);
    unsigned int regval;

    HISI_NET_LOCK_GET(&(ld->tx_lock));

    tx_bq_wr_offset = higmac_readl_bits(ld, TX_BQ_WR_ADDR,
                                        BITS_TX_BQ_WR_ADDR); /* software write pointer */
    tx_bq_rd_offset = higmac_readl_bits(ld, TX_BQ_RD_ADDR,
                                        BITS_TX_BQ_RD_ADDR); /* logic read pointer */

    pos = tx_bq_wr_offset >> DESC_BYTE_SHIFT;
    tmp = tx_bq_wr_offset + DESC_SIZE;
    if (tmp == (HIGMAC_HWQ_TX_BQ_DEPTH << DESC_BYTE_SHIFT)) {
        tmp = 0;
    }
    if (tmp == tx_bq_rd_offset) {
        debug1_printf("\nxhl -- higmac_send error\n");
        debug1_printf("\nxhl -- txbq wr:%08x\n", tx_bq_wr_offset >> DESC_BYTE_SHIFT);
        debug1_printf("\nxhl -- txbq rd:%08x\n", tx_bq_rd_offset >> DESC_BYTE_SHIFT);
        /* we will stop the queue outside of this func */
        return;
    }

    tx_bq_desc = ld->tx_bq.desc + pos;

    higmac_xmit_gso(ld, pos, p, (struct higmac_tso_desc *)tx_bq_desc);

    tx_bq_wr_offset += DESC_SIZE;
    if (tx_bq_wr_offset >= (HIGMAC_HWQ_TX_BQ_DEPTH << DESC_BYTE_SHIFT)) {
        tx_bq_wr_offset = 0;
    }

    HIGMAC_SYNC_BARRIER();
    higmac_writel_bits(ld, tx_bq_wr_offset,
                       TX_BQ_WR_ADDR, BITS_TX_BQ_WR_ADDR);

    HISI_NET_LOCK_PUT(&(ld->tx_lock));
}

static void higmac_deliver(struct los_eth_driver *sc)
{
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)sc->driver_context;
    higmac_priv_t *priv = (higmac_priv_t *)(drv_sc->driver_private);
    UINT32 regval;
    struct higmac_netdev_local *ld = &(g_stHigmacPlatformData[priv->index].stNetdevLocal);

    int rx_bq_wr_offset = 0;
    int rx_bq_rd_offset = 0;
    struct higmac_desc *rx_bq_desc;
    int ret = 0;
    // int start, end;
    unsigned int rlen;
    int pos;

    regval = higmac_readl(ld, RAW_PMU_INT);

    if (1) { // ((regval & TX_RQ_IN_INT) || (regval & TX_RQ_IN_TIMEOUT_INT))
        higmac_xmit_release_skb(ld, sc, priv);
        higmac_writel(ld, TX_RQ_IN_INT | TX_RQ_IN_TIMEOUT_INT, RAW_PMU_INT);
    }
#if 1
    if (1) { // ((regval & RX_BQ_IN_INT) || (regval & RX_BQ_IN_TIMEOUT_INT))
        higmac_writel(ld, RX_BQ_IN_INT | RX_BQ_IN_TIMEOUT_INT, RAW_PMU_INT);

        HISI_NET_LOCK_GET(&(ld->rx_lock));

        rx_bq_wr_offset = higmac_readl_bits(ld, RX_BQ_WR_ADDR,
                                            BITS_RX_BQ_WR_ADDR); /* logic write pointer */
        rx_bq_rd_offset = higmac_readl_bits(ld, RX_BQ_RD_ADDR,
                                            BITS_RX_BQ_RD_ADDR); /* software read pointer */
        while (rx_bq_wr_offset != rx_bq_rd_offset) {
            pos = rx_bq_rd_offset >> DESC_BYTE_SHIFT;
            rx_bq_desc = ld->rx_bq.desc + pos;

            net_dma_cache_inv(rx_bq_desc, sizeof(*rx_bq_desc));

            rlen = rx_bq_desc->data_len;

            if (HIETH_INVALID_RXPKG_LEN(rlen)) {
                int i, desc_id;
                unsigned int *desc = (unsigned int *)rx_bq_desc;

                PRINTK("%s:BAD err recv len=%d, bq_wr=%d, bq_rd=%d\n",
                       __func__, rlen, rx_bq_wr_offset >> DESC_BYTE_SHIFT, rx_bq_rd_offset >> DESC_BYTE_SHIFT);

                for (i = 0; i < sizeof(*rx_bq_desc) / sizeof(int); i++) {
                    PRINTK("0x%x\t", desc[i]);
                }
                PRINTK("\n");

                for (desc_id = 0; desc_id < pos; desc_id++) {
                    desc = (unsigned int *)(ld->rx_bq.desc + desc_id);
                    for (i = 0; i < sizeof(*rx_bq_desc) / sizeof(int); i++) {
                        PRINTK("0x%x\t", desc[i]);
                    }
                    PRINTK("\n");
                }

                PRINTK("%s: bq_wr=%d, bq_rd=%d\n", __func__,
                       higmac_readl_bits(ld, RX_BQ_WR_ADDR, BITS_RX_BQ_WR_ADDR),
                       higmac_readl_bits(ld, RX_BQ_RD_ADDR, BITS_RX_BQ_RD_ADDR));

                while (HIETH_INVALID_RXPKG_LEN(rlen)) {
                    msleep(1);

                    net_dma_cache_inv(rx_bq_desc, sizeof(*rx_bq_desc));
                    rlen = rx_bq_desc->data_len;
                }

                PRINTK("%s: GOOD =======recv len=%d\n", __func__, rlen);
            }

            _eth_drv_recv(sc, rlen);

            rx_bq_rd_offset += DESC_SIZE;
            if (rx_bq_rd_offset >= (HIGMAC_HWQ_RX_BQ_DEPTH << DESC_BYTE_SHIFT)) {
                rx_bq_rd_offset = 0;
            }
        }
        HISI_NET_LOCK_PUT(&(ld->rx_lock));

        higmac_feed_hw(ld, priv);
    }
#endif
    higmac_writel(ld, ~0, RAW_PMU_INT);

    hal_interrupt_unmask(priv->intr_vector);
#ifdef INT_IO_ETH_INT_SUPPORT_REQUIRED
    drv_interrupt_unmask(priv->intr_vector);
#endif
}

static void higmac_recv(struct los_eth_driver *sc,
                        struct eth_drv_sg *sg_list,
                        int sg_len)
{
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)sc->driver_context;
    higmac_priv_t *priv = (higmac_priv_t *)drv_sc->driver_private;
    struct higmac_netdev_local *ld = &(g_stHigmacPlatformData[priv->index].stNetdevLocal);
    int i;
    UINT32 rx_bq_wr_offset;
    UINT32 rx_bq_rd_offset;
    UINT32 bytes_in_buffer;
    UINT32 bytes_in_list = 0;
    UINT32 bytes_needed_list = 0;
    UINT32 buffer_pos = 0;
    UINT32 total_bytes = 0;
    UINT8 *sg_buf;
    UINT8 *hw_rxbuf;
    struct higmac_desc *rx_bq_desc;
    UINT32 pos = 0;
    struct los_sk_buff *skb;
    unsigned int rx_data_len;

    UINT32 rx_fq_wr_offset;
    UINT32 rx_fq_rd_offset;

    rx_bq_wr_offset = higmac_readl_bits(ld, RX_BQ_WR_ADDR,
                                        BITS_RX_BQ_WR_ADDR); /* logic write pointer */
    rx_bq_rd_offset = higmac_readl_bits(ld, RX_BQ_RD_ADDR,
                                        BITS_RX_BQ_RD_ADDR); /* software read pointer */

    pos = rx_bq_rd_offset >> DESC_BYTE_SHIFT;
    rx_bq_desc = ld->rx_bq.desc + pos;

    rx_data_len = rx_bq_desc->data_len;

    skb = ld->rx_fq.skb[pos];

    net_dma_cache_inv(skb->data, rx_data_len);

    for (i = 0; i < sg_len; ++i) {
        bytes_in_list = 0;
        while (bytes_in_list < sg_list[i].len) {
            bytes_needed_list = sg_list[i].len - bytes_in_list;
            bytes_in_buffer = rx_data_len;
            bytes_in_buffer -= buffer_pos;
            sg_buf = (UINT8 *)(sg_list[i].buf);
            hw_rxbuf = skb->data;
            if (bytes_needed_list < bytes_in_buffer) {
                if (sg_buf) {
                    memcpy(&sg_buf[bytes_in_list],
                           (UINT8 *)&hw_rxbuf[buffer_pos],
                           bytes_needed_list);
                }
                bytes_in_list += bytes_needed_list;
                buffer_pos += bytes_needed_list;
                total_bytes += bytes_needed_list;
            } else {
                if (sg_buf) {
                    memcpy(&sg_buf[bytes_in_list],
                           (UINT8 *)&hw_rxbuf[buffer_pos],
                           bytes_in_buffer);
                }

                rx_bq_rd_offset += DESC_SIZE;
                if (rx_bq_rd_offset >= (HIGMAC_HWQ_RX_BQ_DEPTH << DESC_BYTE_SHIFT)) {
                    rx_bq_rd_offset = 0;
                }
                higmac_writel_bits(ld, rx_bq_rd_offset, RX_BQ_RD_ADDR,
                                   BITS_RX_BQ_RD_ADDR);
                bytes_in_list += bytes_in_buffer;
                total_bytes += bytes_in_buffer;
                buffer_pos = 0;
            }
        }  // while (bytes_in_list < sg_list[i].len)
    }  // for(i = 0; i < sg_len; ++i)
}

static void higmac_poll(struct los_eth_driver *sc)
{
    higmac_deliver(sc);
}

static int higmac_int_vector(struct los_eth_driver *sc)
{
#ifdef HIGMAC1_ENABLE
    return NUM_HAL_INTERRUPT_GSF1;
#else
    return NUM_HAL_INTERRUPT_GSF;
#endif
}

// just used for testsuit and demo
#ifdef HIGMAC1_ENABLE
extern struct platform_device higmac1_platform_device;
void get_defaultNetif(struct netif **pnetif)
{
    *pnetif = &(((struct los_eth_driver *)(higmac1_platform_device.dev.driver_data))->ac_if);
}
#else
extern struct platform_device higmac_platform_device;
void get_defaultNetif(struct netif **pnetif)
{
    *pnetif = &(((struct los_eth_driver *)(higmac_platform_device.dev.driver_data))->ac_if);
}
#endif

// /////////////////////////////////////////////////////////////////////////////
// /used for netif
// ////////////////////////////////////////////////////////////////////////////

// This function is called from a hardware driver to indicate that an input
// packet has arrived.  The routine will set up appropriate network resources
// to hold the data and call back into the driver to retrieve the data.
static void eth_drv_recv(struct los_eth_driver *sc, int total_len)
{
    struct eth_drv_sg sg_list[MAX_ETH_DRV_SG];
    struct netif *netif = &sc->ac_if;
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)sc->driver_context;
    struct pbuf *p, *q;

    int sg_len = 0;

    if ((total_len > MAX_ETH_MSG) || (total_len < 0)) {
        total_len = MAX_ETH_MSG;
    }

    p = pbuf_alloc(PBUF_RAW, (total_len + ETH_PAD_SIZE), PBUF_RAM);

    if (p == NULL) {
        PRINTK("eth_drv_recv : pbuf_alloc failed\n");
        return;
    }

#if ETH_PAD_SIZE
    /* drop the padding word */
    if (pbuf_header(p, -ETH_PAD_SIZE)) {
        PRINTK("[HIGMAC_ERROR]eth_drv_recv : pbuf_header drop failed\n");
        pbuf_free(p);
        return;
    }
#endif

    for (q = p; q != NULL; q = q->next) {
        sg_list[sg_len].buf = (AARCHPTR)q->payload;
        sg_list[sg_len++].len = q->len;
    }

   (drv_sc->funs->recv)(sc, sg_list, sg_len);

#if ETH_PAD_SIZE
    /* reclaim the padding word */
    if (pbuf_header(p, ETH_PAD_SIZE)) {
        PRINTK("[HIGMAC_ERROR]eth_drv_recv : pbuf_header reclaim failed\n");
        pbuf_free(p);
        return;
    }
#endif

    driverif_input(netif, p);
}

void eth_drv_send(struct netif *netif, struct pbuf *p)
{
    struct los_eth_driver *sc = (struct los_eth_driver *)netif->state;
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)sc->driver_context;
    higmac_priv_t *priv = (higmac_priv_t *)drv_sc->driver_private;

    hal_interrupt_mask(priv->intr_vector);
    while (!(drv_sc->funs->can_send)(sc))
        ;
   (drv_sc->funs->send)(sc, p);

    hal_interrupt_unmask(priv->intr_vector);
}

static void eth_drv_init(struct los_eth_driver *sc, unsigned char *enaddr)
{
    struct netif *netif = &sc->ac_if;
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)sc->driver_context;
    struct netif *tmp_netif = NULL;
    ip4_addr_t ipaddr, netmask, gw;

    IP4_ADDR(&gw, 192, 168, 1, 1);
    IP4_ADDR(&ipaddr, 192, 168, 1, 2);
    IP4_ADDR(&netmask, 255, 255, 255, 0);

    netif->state = sc;
    netif->drv_send = eth_drv_send;
    netif->drv_set_hwaddr = higmac_set_hwaddr;
#if PF_PKT_SUPPORT
    netif->drv_config = higmac_config_mode;
#endif
#if LWIP_NETIF_ETHTOOL
    netif->ethtool_ops = NULL;
#endif
    netif->link_layer_type = ETHERNET_DRIVER_IF;
    netif->hwaddr_len = ETHER_ADDR_LEN;
    netif_set_default(netif_add(netif, &ipaddr, &netmask, &gw));
#if LWIP_TX_CSUM_OFFLOAD
    netif->flags |= NETIF_FLAG_DRIVER_CSUM_SUPPORT;
#endif

    // enaddr == 0 -> hardware init was incomplete (no ESA)
    if (enaddr != 0) {
        // Set up hardware address
        memcpy(netif->hwaddr, enaddr, ETHER_ADDR_LEN);
        // Perform any hardware initialization
       (drv_sc->funs->start)(sc, (unsigned char *)&netif->hwaddr, 0);
    }
#ifdef DRVSEM_HAL_VIRTUAL_VECTOR_DIAG
    // Set up interfaces so debug environment can share this device
    {
        void *dbg = DRVACC_CALL_IF_DBG_DATA();
        if (!dbg) {
            DRVACC_CALL_IF_DBG_DATA_SET((void *)sc);
        }
    }
#endif
    //
    // we call this after the driver was started successfully
    //
    // lwip_dhcp_init(netif);

    netif_set_link_down(netif);
    netif_set_link_callback(netif, &higmac_link_status_changed);
}

struct los_eth_funs eth_drv_funs_higmac = { eth_drv_init, eth_drv_recv, NULL };

static const char *get_phy_speed_string(int speed)
{
    switch (speed) {
        case PHY_SPEED_10:
            return "10Mbps";
        case PHY_SPEED_100:
            return "100Mbps";
        case PHY_SPEED_1000:
            return "1Gbps";
        case PHY_SPEED_UNKNOWN:
            return "Unknown";
        default:
            return "Unsupported";
    }
}

void higmac_link_status_changed(struct netif *netif)
{
    struct los_eth_driver *sc = (struct los_eth_driver *)netif->state;
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)sc->driver_context;
    higmac_priv_t *priv = (higmac_priv_t *)drv_sc->driver_private;
    struct higmac_netdev_local *ld = &(g_stHigmacPlatformData[priv->index].stNetdevLocal);
    int val;

    int duplex;
    int speed;

    duplex = miiphy_duplex(ld, priv->phy);
    speed = miiphy_speed(ld, priv->phy);

    switch (priv->phy->phy_mode) {
        case PHY_INTERFACE_MODE_RGMII:
            if (speed == PHY_SPEED_1000) {
                val = RGMII_SPEED_1000;
            }
            else if (speed == PHY_SPEED_100) {
                val = RGMII_SPEED_100;
            }
            else {
                val = RGMII_SPEED_10;
            }
            break;
        case PHY_INTERFACE_MODE_MII:
            if (speed == PHY_SPEED_100) {
                val = MII_SPEED_100;
            }
            else {
                val = MII_SPEED_10;
            }
            break;
        case PHY_INTERFACE_MODE_RMII:
            if (speed == PHY_SPEED_100) {
                val = RMII_SPEED_100;
            }
            else {
                val = RMII_SPEED_10;
            }
            break;
        default:
            PRINTK("not supported mode: %d\n", priv->phy->phy_mode);
            val = MII_SPEED_10;
            break;
    }

    if (duplex) {
        val |= GMAC_FULL_DUPLEX;
    }

    higmac_set_macif(ld, val, speed);
    writel(duplex, ld->gmac_iobase + MAC_DUPLEX_HALF_CTRL);

    if (netif_is_link_up(netif))
        PRINTK("Link is Up - %s/%s\n",
               get_phy_speed_string(speed),
               PHY_DUPLEX_FULL == duplex ? "Full" : "Half");
    else {
        PRINTK("Link is Down\n");
    }
}

void phy_state_machine(unsigned long arg)
{
    struct los_eth_driver *sc = (struct los_eth_driver *)arg;
    struct netif *netif = &sc->ac_if;
    struct eth_drv_sc *drv_sc = (struct eth_drv_sc *)sc->driver_context;
    higmac_priv_t *priv = (higmac_priv_t *)drv_sc->driver_private;

    struct higmac_netdev_local *ld = &(g_stHigmacPlatformData[priv->index].stNetdevLocal);

    int link_status;

    link_status = miiphy_link(ld, priv->phy);
    if (netif_is_link_up(netif) && !link_status) {
        netif_set_link_down(netif);
    }
    else if (!netif_is_link_up(netif) && link_status) {
        netif_set_link_up(netif);
    }

    mod_timer(&priv->phy_timer, LOS_MS2Tick(PHY_STATE_TIME));
}

u8_t higmac_set_hwaddr(struct netif *netif, u8_t *addr, u8_t len)
{
    struct los_eth_driver *sc = LOS_DL_LIST_ENTRY(netif, struct los_eth_driver, ac_if);
    higmac_priv_t *priv = (higmac_priv_t *)((struct eth_drv_sc *)sc->driver_context)->driver_private;

    if (is_multicast_ether_addr(addr)) {
        PRINTK("WARN: config a muticast mac address, please check!\n");
        return -1;
    }

    if (len != ETHER_ADDR_LEN) {
        PRINTK("WARN: config wrong mac address len=%u\n", len);
        return -1;
    }

    higmac_hw_set_mac_addr(&(g_stHigmacPlatformData[priv->index].stNetdevLocal), addr);

    memcpy(netif->hwaddr, addr, len);

    return 0;
}

#if PF_PKT_SUPPORT
void higmac_config_mode(struct netif *netif, uint32_t config_flags, u8_t setBit)
{
    struct los_eth_driver *sc = LOS_DL_LIST_ENTRY(netif, struct los_eth_driver, ac_if);
    higmac_priv_t *priv = (higmac_priv_t *)((struct eth_drv_sc *)sc->driver_context)->driver_private;
    struct higmac_netdev_local *ld = &(g_stHigmacPlatformData[priv->index].stNetdevLocal);

    if (config_flags & IFF_PROMISC) {
        higmac_writel_bits(ld, !setBit, REC_FILT_CONTROL, BIT_MC_MATCH_EN);
        higmac_writel_bits(ld, !setBit, REC_FILT_CONTROL, BIT_UC_MATCH_EN);
    }

    if (config_flags & IFF_ALLMULTI) {
        higmac_writel_bits(ld, !setBit, REC_FILT_CONTROL, BIT_MC_MATCH_EN);
    }
}
#endif

struct los_eth_driver higmac_drv_sc;

int hisi_eth_set_phy_mode(const char *phy_mode)
{
    int i;

    for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++)
        if (!strcasecmp(phy_mode, phy_modes(i))) {
            g_user_set_phy_mode = PHY_MODE_SET;
            g_phy_mode_val = i;
            return 0;
        }

    return -1;
}

int hisi_eth_set_phy_addr(unsigned int phy_addr)
{
    if (phy_addr > MAX_PHY_ADDR) {
        return -1;
    }

    g_user_set_phy_addr = PHY_ADDR_SET;
    g_phy_addr_val = phy_addr;

    return 0;
}

static struct eth_hwr_funs g_stEthnetDrvFun = {
    .start = higmac_start,
    .stop = higmac_stop,
    .control = higmac_control,
    .can_send = higmac_can_send,
    .send = higmac_send,
    .recv = higmac_recv,
    .deliver = higmac_deliver,
    .poll = higmac_poll,
    .int_vector = higmac_int_vector,
    .eth_drv = &eth_drv_funs_higmac,
    .eth_drv_old = (struct los_eth_funs *)0,

};

static int higmac_init(struct platform_device *dev)
{
    higmac_priv_t *pstPrivData = NULL;
    struct los_eth_driver *pstDrvData = NULL;
    struct eth_drv_sc *pstDrvSc = NULL;
    struct resource *res = NULL;

    // ////////////////////////////////////////////////////////
    // init drv_data, changed useful hisi_eth_drv_sc to be drv context
    // ////////////////////////////////////////////////////////
    pstPrivData = (higmac_priv_t *)malloc(sizeof(higmac_priv_t));
    if (NULL == pstPrivData) {
        debug1_printf("higmac_priv_s malloc error!\n");
        return LOS_NOK;
    }

    pstPrivData->index = dev->id;

    res = platform_get_resource(dev, IORESOURCE_IRQ, 0);
    if (NULL == res) {
        debug1_printf("platform_get_resource IORESOURCE_IRQ error!\n");
        goto free_pstDrvData;
    }
    pstPrivData->intr_vector = res->start;

    res = platform_get_resource(dev, IORESOURCE_MEM, 0);
    if (NULL == res) {
        debug1_printf("platform_get_resource IORESOURCE_MEM error!\n");
        goto free_pstDrvData;
    }
    pstPrivData->base = res->start;

    pstPrivData->phy = (eth_phy_access_t *)malloc(sizeof(eth_phy_access_t));
    if (NULL == pstPrivData->phy) {
        debug1_printf("eth_phy_access_t malloc error!\n");
        goto free_pstDrvData;
    }

    pstPrivData->phy->init_done = false;
    pstPrivData->phy->init = NULL;
    pstPrivData->phy->reset = NULL;
    pstPrivData->total_len = 0;
    pstDrvData = (struct los_eth_driver *)malloc(sizeof(struct los_eth_driver));
    if (NULL == pstDrvData) {
        debug1_printf("los_eth_driver malloc error!\n");
        goto free_pstPrivData_phy;
    }

    pstDrvSc = (struct eth_drv_sc *)malloc(sizeof(struct eth_drv_sc));
    if (NULL == pstDrvSc) {
        debug1_printf("eth_drv_sc malloc error!\n");
        goto free_pstDrvData;
    }
    pstDrvSc->dev_name = "eth1";
    pstDrvSc->funs = &g_stEthnetDrvFun;
    pstDrvSc->driver_private = (void *)pstPrivData;

    pstDrvData->driver_context = (void *)pstDrvSc;
    dev->dev.driver_data = (void *)pstDrvData;

    // ////////////////////////////////////////////////////////
    // higmac_hw_init
    // ////////////////////////////////////////////////////////
    g_stHigmacPlatformData = (struct higmac_platform_data *)dev_get_platdata(&(dev->dev));
    LOS_EventInit(&(g_stHigmacPlatformData[pstPrivData->index].stEvent));

    higmac_hw_init(dev);

    return LOS_OK;

free_pstDrvData:
    free((void *)pstDrvData);
    pstDrvData = NULL;
free_pstPrivData_phy:
    free((void *)pstPrivData->phy);
    pstPrivData->phy = NULL;
free_pstPrivData:
    free((void *)pstPrivData);
    pstPrivData = NULL;
    return LOS_NOK;
}

static int higmac_deinit(struct platform_device *dev)
{
    struct los_eth_driver *pstDrvData = NULL;
    struct eth_drv_sc *pstDrvSc = NULL;
    higmac_priv_t *pstPrivData = NULL;
    /* TODO: HW deinit */

    pstDrvData = (struct los_eth_driver *)dev_get_drvdata(&(dev->dev));
    if (NULL == pstDrvData->driver_context) {
        debug1_printf("higmac has not been init!\n");
    } else {
        pstDrvSc = (struct eth_drv_sc *)pstDrvData->driver_context;
        pstPrivData = (higmac_priv_t *)pstDrvSc->driver_private;

        free(pstPrivData->phy);
        pstPrivData->phy = NULL;
        free(pstPrivData);
        pstDrvSc->driver_private = NULL;
        free(pstDrvSc);
        pstDrvData->driver_context = NULL;
    }

    return LOS_OK;
}

static int ethnet_higmac_suspend(struct device *dev)
{
    return 0;
}

static int ethnet_higmac_resume(struct device *dev)
{
    return 0;
}

static const struct dev_pm_op ethnet_higmac_dev_pm_ops = {
    .suspend = ethnet_higmac_suspend,
    .resume = ethnet_higmac_resume,
};

static int ethnet_higmac_probe(struct platform_device *dev)
{
    return higmac_init(dev);
}

static int ethnet_higmac_remove(struct platform_device *dev)
{
    return higmac_deinit(dev);
}

static struct platform_driver higmac_platform_driver = {
    .probe = ethnet_higmac_probe,
    .remove = ethnet_higmac_remove,
    .driver = {
        .name = HIGMAC_DRIVER_NAME,
        .pm = &ethnet_higmac_dev_pm_ops,
    },
};

int /* __init */ ethnet_higmac_init(void)
{
    int ret = 0;
    ret = platform_driver_register(&higmac_platform_driver);
    if (ret) {
        return LOS_NOK;
    }

    return ret;
}

void /* __exit */ ethnet_higmac_exit(void)
{
    platform_driver_unregister(&higmac_platform_driver);
}

module_init(ethnet_higmac_init);
module_exit(ethnet_higmac_exit);
